﻿using System;
using Bettzueche.RLGlue.RLComponent;
using System.Diagnostics;
using Bettzueche.RLGlue.RLTypes;

namespace Bettzueche.RLGlue.Codec.Network {
    /// <summary>
    /// Middleware interface for the agent in RLGlue context.
    /// </summary>
    /// <remarks>
    /// This Code is based on the Java RL-Glue <a href="http://rl-glue-ext.googlecode.com/">rl-glue-ext.googlecode.com/</a>,
    /// Copyright (C) 2007, Brian Tanner.
    /// <para>
    /// The ClientAgent is used by the AgentLoader, for example, and encapsulates the network communication.
    /// For running an agent implementation in your experiment, you can use the <see cref="Bettzueche.RLGlue.Codec.Util.AgentLoader"/> or
    /// this ClientAgent like folling example:
    /// </para>
    /// </remarks>
    /// <example>
    /// <code>
    /// ClientAgent theClient = new ClientAgent(myAgentImplInstance);
    /// int retryTimeout = 2;
    /// theClient.Connect(host, port, retryTimeout);
    /// theClient.RunAgentEventLoop();
    /// theClient.Close();     
    /// </code>
    /// </example>
    public sealed class ClientAgent : AbstractClientAgent{

        /// <summary>Reference to the agent implementation</summary>
        private IAgent theAgent;


        /// <summary>
        /// New instance of this agent middleware.
        /// </summary>
        /// <param name="theAgent">Your Agent implementation</param>
        /// <exception cref="ArgumentNullException"></exception>
        public ClientAgent(IAgent theAgent) {
            if (theAgent == null)
                throw new ArgumentNullException();
            this.theAgent = theAgent;
            this.network = new Network();
        }


        protected override void onAgentMessage() {
            String message = network.GetString();
            String reply = theAgent.MessageAgent(message);

            network.ClearSendBuffer();
            network.PutInt(Protocol.kAgentMessage);
            network.PutInt(Network.SizeOf(reply));
            network.PutString(reply);
        }

        /// <summary>
        /// Handler for a AgentCleanup message from RLGlue.exe.
        /// </summary>
        protected override void onAgentCleanup() {
            theAgent.CleanupAgent();

            network.ClearSendBuffer();
            network.PutInt(Protocol.kAgentCleanup);
            network.PutInt(0); // No data in this packet
        }

        /// <summary>
        /// Handler for a AgentEnd message from RLGlue.exe.
        /// </summary>
        protected override void onAgentEnd() {
            double reward = network.GetDouble();

            theAgent.EndAgent(reward);

            network.ClearSendBuffer();
            network.PutInt(Protocol.kAgentEnd);
            network.PutInt(0); // No data in this packet
        }

        /// <summary>
        /// Handler for a AgentStep message from RLGlue.exe.
        /// </summary>
        protected override void onAgentStep() {
            double reward = network.GetDouble();
            GeneralState observation = network.GetObservation();
            GeneralAction action = theAgent.StepAgent(reward, observation);

            int size = Network.SizeOf(action);
            network.ClearSendBuffer();
            network.PutInt(Protocol.kAgentStep);
            network.PutInt(size);
            network.PutAction(action);
        }

        /// <summary>
        /// Handler for a AgentStart message from RLGlue.exe.
        /// </summary>
        protected override void onAgentStart() {
            Debug.WriteLine("\tonAgentStart()");
            GeneralState observation = network.GetObservation();
            Debug.WriteLine("\t\tgot observation: " + observation);
            GeneralAction action = theAgent.StartAgent(observation);
            Debug.WriteLine("\t\tgot action");

            int size = Network.SizeOf(action);

            network.ClearSendBuffer();
            network.PutInt(Protocol.kAgentStart);
            network.PutInt(size);
            network.PutAction(action);
        }

        /// <summary>
        /// Handler for a AgentInit message from RLGlue.exe.
        /// </summary>
        protected override void onAgentInit() {
            string taskSpec = network.GetString();
            theAgent.InitAgent(taskSpec);

            network.ClearSendBuffer();
            network.PutInt(Protocol.kAgentInit);
            //BTANNER: Sept 16 2008.. why are we putting this here?
            //OH, it's the size!  Ok.  I think.
            network.PutInt(0); // No data following this header
        }
    }
}
